/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include ".\mwinsnmp.h"

//--------------------------------------------------------------------

MWinSNMP::MWinSNMP(void)
{
	hSession = SNMPAPI_FAILURE;
}

MWinSNMP::~MWinSNMP(void)
{
	//SnmpDestroySession(); - o distruge ScanMethodSnmp
	SnmpDestroy();
}

//--------------------------------------------------------------------

bool MWinSNMP::SnmpInit(void)
{
	SNMPAPI_STATUS status;
	smiUINT32 majVer;
	smiUINT32 minVer;
	smiUINT32 level;
	smiUINT32 translateMode;
	smiUINT32 retransmitMode;

	status = SnmpStartup(&majVer, &minVer, &level, &translateMode, &retransmitMode);
	if(status!=SNMPAPI_SUCCESS) return false;

	status = SnmpSetTranslateMode(SNMPAPI_UNTRANSLATED_V2);
	//status = SnmpSetTranslateMode(SNMPAPI_TRANSLATED);
	if(status!=SNMPAPI_SUCCESS){
		SnmpDestroy();
		return false;
	}

	status = SnmpSetRetransmitMode(SNMPAPI_OFF);
	if(status!=SNMPAPI_SUCCESS){
		SnmpDestroy();
		return false;
	}

	return true;
}

void MWinSNMP::SnmpDestroy(void)
{
	SNMPAPI_STATUS status;

	status = SnmpCleanup();
	if(status!=SNMPAPI_SUCCESS){
		// error occured
		status = status;
	}
}

//--------------------------------------------------------------------

bool MWinSNMP::SnmpInitSession(SNMPAPI_CALLBACK SnmpCallbackFunction, void* clientDataIn)
{
	//SnmpDestroySession(); - o distruge ScanMethodSnmp

	hSession = SnmpCreateSession(NULL, 0, SnmpCallbackFunction, clientDataIn);
	if(hSession==SNMPAPI_FAILURE)
		return false;

	return true;
}

HSNMP_SESSION MWinSNMP::GetCurrentSessionHandle(void)
{
	return hSession;
}

void MWinSNMP::SnmpDestroySession(void)
{
	SnmpDestroySession(hSession);

	hSession = SNMPAPI_FAILURE;
}

void MWinSNMP::SnmpDestroySession(HSNMP_SESSION hSessionIn)
{
	if(hSessionIn==SNMPAPI_FAILURE) return;

	SnmpClose(hSessionIn);

	hSession = SNMPAPI_FAILURE;
}

//--------------------------------------------------------------------

bool MWinSNMP::SendSystemQuery(CString srcIpIn, CString dstIpIn, CString communityNameIn)
{
	SNMPAPI_STATUS status;

	HSNMP_ENTITY   heSrc;
	HSNMP_ENTITY   heDst;
	smiOCTETS      oCtx;
	HSNMP_CONTEXT  hCtx;

	smiUINT32      oidSystem[9];

	smiOID         oidName;
	smiVALUE       oidValue;
	HSNMP_VBL      varBindList;
	int            i, oidCnt;

	HSNMP_PDU      hPdu;

	if(hSession==SNMPAPI_FAILURE) return false;

	/* According to RFC 1213
		1.3.6.1.2.1.1.1 = org.dod.internet.mgmt.mib-2.system.sysDescr
			SNMP_SYNTAX_OCTETS
			smiOCTETS smiValue.string
			"A textual description of the entity.  This value should include the full name and version identification of the system's hardware type,
			 software operating-system, and networking software.  It is mandatory that this only contain printable ASCII characters."

		1.3.6.1.2.1.1.2 = org.dod.internet.mgmt.mib-2.system.sysObjectID
			SNMP_SYNTAX_OID
			smiOID smiValue.oid
			"The vendor's authoritative identification of the network management subsystem contained in the
			 entity.  This value is allocated within the SMI enterprises subtree (1.3.6.1.4.1) and provides an
			 easy and unambiguous means for determining `what kind of box' is being managed."

		1.3.6.1.2.1.1.3 = org.dod.internet.mgmt.mib-2.system.sysUpTime
			SNMP_SYNTAX_TIMETICKS
			smiUINT32 smiValue.uNumber
			"The time (in hundredths of a second) since the network management portion of the system was last re-initialized."


		1.3.6.1.2.1.1.4 = org.dod.internet.mgmt.mib-2.system.sysContact
			SNMP_SYNTAX_OCTETS
			smiOCTETS smiValue.string
			"The textual identification of the contact person for this managed node, together with information on how to contact this person."


		1.3.6.1.2.1.1.5 = org.dod.internet.mgmt.mib-2.system.sysName
			SNMP_SYNTAX_OCTETS
			smiOCTETS smiValue.string
			"An administratively-assigned name for this managed node.  By convention, this is the node's fully-qualified domain name."


		1.3.6.1.2.1.1.6 = org.dod.internet.mgmt.mib-2.system.sysLocation
			SNMP_SYNTAX_OCTETS
			smiOCTETS smiValue.string
			"The physical location of this node (e.g., `telephone closet, 3rd floor')."


		1.3.6.1.2.1.1.7 = org.dod.internet.mgmt.mib-2.system.sysServices
			SNMP_SYNTAX_INT
			smiINT smiValue.sNumber
			"A value which indicates the set of services that this entity primarily offers.
			 The value is a sum.  This sum initially takes the value zero, Then, for each layer, L, in the range
			 1 through 7, that this node performs transactions for, 2 raised to (L - 1) is added to the sum.  For
			 example, a node which performs primarily routing functions would have a value of 4 (2^(3-1)).  In
			 contrast, a node which is a host offering application services would have a value of 72
			 (2^(4-1) + 2^(7-1)).  Note that in the context of the Internet suite of protocols, values should be
			 calculated accordingly:
				layer  functionality
					1  physical (e.g., repeaters)
					2  datalink/subnetwork (e.g., bridges)
					3  internet (e.g., IP gateways)
					4  end-to-end  (e.g., IP hosts)
					7  applications (e.g., mail relays)
			 For systems including OSI protocols, layers 5 and 6 may also be counted."
	*/
	oidSystem[0] = 1;
	oidSystem[1] = 3;
	oidSystem[2] = 6;
	oidSystem[3] = 1;
	oidSystem[4] = 2;
	oidSystem[5] = 1;
	oidSystem[6] = 1;
	oidSystem[7] = 0;
	oidSystem[8] = 0;

	heSrc = SnmpStrToEntity(hSession, srcIpIn);
	if(heSrc==SNMPAPI_FAILURE) return false;

	heDst = SnmpStrToEntity(hSession, dstIpIn);
	if(heDst==SNMPAPI_FAILURE){
		SnmpFreeEntity(heSrc);
		return false;
	}

	oCtx.len = communityNameIn.GetLength();
	oCtx.ptr = (BYTE*)(const char*)communityNameIn;
	hCtx = SnmpStrToContext(hSession, &oCtx);
	if(hCtx==SNMPAPI_FAILURE){
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}

	oidName.len = 9;
	oidName.ptr = oidSystem;

	oidValue.syntax = SNMP_SYNTAX_NULL;
	oidValue.value.uNumber = 0;

	varBindList = SnmpCreateVbl(hSession, NULL, NULL);
	if(varBindList==SNMPAPI_FAILURE){
		SnmpFreeContext(hCtx);
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}
	
	oidCnt = 7;
	for(i=1; i<=oidCnt; i++){
		oidSystem[7] = i;	// modific doar a 7-a pozitie (coincidenta cu oidCnt==7 de mai sus)
		status = SnmpSetVb(varBindList, 0, &oidName, &oidValue);
		if(status==SNMPAPI_FAILURE){
			SnmpFreeVbl(varBindList);
			SnmpFreeContext(hCtx);
			SnmpFreeEntity(heDst);
			SnmpFreeEntity(heSrc);
			return false;
		}
	}

	hPdu = SnmpCreatePdu(hSession, SNMP_PDU_GET, NULL, 0, 0, varBindList);
	if(hPdu==SNMPAPI_FAILURE){
		SnmpFreeVbl(varBindList);
		SnmpFreeContext(hCtx);
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}

	status = SnmpSendMsg(hSession, heSrc, heDst, hCtx, hPdu);
	if(status!=SNMPAPI_SUCCESS){
		SnmpFreePdu(hPdu);
		SnmpFreeVbl(varBindList);
		SnmpFreeContext(hCtx);
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}

	return true;
}

//--------------------------------------------------------------------

bool MWinSNMP::RecvSystemQuery(HSNMP_SESSION hSessionIn)
{
	SNMPAPI_STATUS status;
	HSNMP_ENTITY   heSrc;
	HSNMP_ENTITY   heDst;
	HSNMP_CONTEXT  hCtx;
	HSNMP_PDU      hPdu;

	smiINT    pduType;
	smiINT32  reqId;
	smiINT    errStatus;
	smiINT    errIdx;
	HSNMP_VBL varBindList;

	smiUINT32 i, vblCnt;
	smiOID    oidName;
	smiVALUE  oidValue;
	char      sOidName[MAXOBJIDSTRSIZE];
	CString   str;

	status = SnmpRecvMsg(hSessionIn, &heSrc, &heDst, &hCtx, &hPdu);
	if(status!=SNMPAPI_SUCCESS) return false;

	status = SnmpGetPduData(hPdu, &pduType, &reqId, &errStatus, &errIdx, &varBindList);
	if(status!=SNMPAPI_SUCCESS){
		SnmpFreePdu(hPdu);
		SnmpFreeContext(hCtx);
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}

	vblCnt = SnmpCountVbl(varBindList);
	if(vblCnt==SNMPAPI_FAILURE){
		SnmpFreeVbl(varBindList);
		SnmpFreePdu(hPdu);
		SnmpFreeContext(hCtx);
		SnmpFreeEntity(heDst);
		SnmpFreeEntity(heSrc);
		return false;
	}

	xSystemQueryResult.Initialize();

	for(i=0; i<vblCnt; i++){
		status = SnmpGetVb(varBindList, i+1, &oidName, &oidValue);
		if(status==SNMPAPI_SUCCESS){

			// process oidName and oidValue
			memset(sOidName, 0, sizeof(sOidName));
			status = SnmpOidToStr(&oidName, sizeof(sOidName), sOidName);
			if(status!=SNMPAPI_FAILURE){
				// sOidName is OK
				str = sOidName;

				try{
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.1.0"))==0) xSystemQueryResult.sysDescription = ValueToString   (oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.2.0"))==0) xSystemQueryResult.sysVendorOid   = ValueToOidString(oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.3.0"))==0) xSystemQueryResult.sysUpTime      = ValueToULong    (oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.4.0"))==0) xSystemQueryResult.sysContact     = ValueToString   (oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.5.0"))==0) xSystemQueryResult.sysName        = ValueToString   (oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.6.0"))==0) xSystemQueryResult.sysLocation    = ValueToString   (oidValue);
					if(str.CompareNoCase(_T("1.3.6.1.2.1.1.7.0"))==0) xSystemQueryResult.sysServices    = ValueToLong     (oidValue);
				}
				catch(...){
					xSystemQueryResult.Initialize();
					xSystemQueryResult.sysDescription = _T("Error receiving data!");
				}
			}

			SnmpFreeDescriptor(SNMP_SYNTAX_OCTETS, (smiLPOPAQUE)&oidName.ptr);
			SnmpFreeDescriptor (oidValue.syntax, (smiLPOPAQUE)&oidValue.value);
		}
	}

	SnmpFreeVbl(varBindList);
	SnmpFreePdu(hPdu);
	SnmpFreeContext(hCtx);
	SnmpFreeEntity(heDst);
	SnmpFreeEntity(heSrc);
	return true;
}

//--------------------------------------------------------------------

long MWinSNMP::ValueToLong(smiVALUE valIn)
{
	switch(valIn.syntax){
		case SNMP_SYNTAX_INT :
//		case SNMP_SYNTAX_INT32 :
			break;

		default:
			return 0;
	}

	return (long)valIn.value.sNumber;
}

unsigned long MWinSNMP::ValueToULong(smiVALUE valIn)
{
	switch(valIn.syntax){
		case SNMP_SYNTAX_UINT32 :
		case SNMP_SYNTAX_CNTR32 :
		case SNMP_SYNTAX_GAUGE32 :
		case SNMP_SYNTAX_TIMETICKS :
			break;

		default:
			return 0;
	}

	return (unsigned long)valIn.value.uNumber;
}

CString MWinSNMP::ValueToString(smiVALUE valIn)
{
	CString str;
	int i, cnt;

	str.Empty();
	if(valIn.syntax!=SNMP_SYNTAX_OCTETS)
		return str;

	if(valIn.value.string.ptr==NULL)
		return str;

	memset(tempBuffer, 0, sizeof(tempBuffer));
	cnt = valIn.value.string.len;
	for(i=0; i<cnt && i<MAXOBJIDSTRSIZE; i++)
		tempBuffer[i] = valIn.value.string.ptr[i];
	
	str = tempBuffer;
	return str;
}

CString MWinSNMP::ValueToOidString(smiVALUE valIn)
{
	SNMPAPI_STATUS status;
	CString str;

	str.Empty();
	if(valIn.syntax!=SNMP_SYNTAX_OID)
		return str;

	memset(tempBuffer, 0, sizeof(tempBuffer));
	status = SnmpOidToStr(&valIn.value.oid, MAXOBJIDSTRSIZE, tempBuffer);
	if(status==SNMPAPI_FAILURE)
		return str;

	str = tempBuffer;
	return str;
}

//--------------------------------------------------------------------
